package com.kakarote.core.utils;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.kakarote.core.common.Const;
import com.kakarote.core.common.cache.AdminCacheKey;
import com.kakarote.core.common.cache.CrmCacheKey;
import com.kakarote.core.common.cache.JxcCacheKey;
import com.kakarote.core.common.cache.ScrmCacheKey;
import com.kakarote.core.entity.AdminCompanySecuritySettingVO;
import com.kakarote.core.entity.UserExtraInfo;
import com.kakarote.core.entity.UserInfo;
import com.kakarote.core.redis.Redis;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.Cookie;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author z
 * 用户操作相关方法
 */
@Slf4j
public class UserUtil {

    private static ThreadLocal<UserInfo> threadLocal = new ThreadLocal<>();


    public static UserInfo getUser() {
        return threadLocal.get();
    }

    public static Long getUserId() {
        return Optional.ofNullable(threadLocal.get()).orElse(new UserInfo()).getUserId();
    }


    public static void setUser(UserInfo adminUser) {
        threadLocal.set(adminUser);
    }

    public static UserInfo setUser(Long userId) {
        UserInfo userInfo = UserCacheUtil.getUserInfo(userId);
        setUser(userInfo);
        return userInfo;
    }


    public static void removeUser() {
        threadLocal.remove();
    }

    /**
     * 验证签名是否正确
     *
     * @param key  key
     * @param salt 盐
     * @param sign 签名
     * @return 是否正确 true为正确
     */
    public static boolean verify(String key, String salt, String sign) {
        return sign.equals(sign(key, salt)) || sign.equals(signP(key, salt)) || sign.equals(signP2(key, salt));
    }

    /**
     * 签名数据
     *
     * @param key  key
     * @param salt 盐
     * @return 加密后的字符串
     */
    public static String sign(String key, String salt) {
        return SecureUtil.md5(key.concat("erp").concat(salt));
    }

    /**
     * 签名数据
     * PHP端签名
     *
     * @param key  key
     * @param salt 盐
     * @return 加密后的字符串
     */
    private static String signP(String key, String salt) {
        String username = key.substring(0, 11);
        String password = key.substring(11);
        return SecureUtil.md5(SecureUtil.md5(SecureUtil.sha1(username.concat(password))) + SecureUtil.md5(password.concat(salt)));
    }

    private static String signP2(String key, String salt) {
        String username = key.substring(0, 11);
        String password = key.substring(11);
        return SecureUtil.md5(SecureUtil.sha1(password) + SecureUtil.md5(password.concat(salt)));
    }

    public static void userExpire(String token, UserInfo userInfo) {
        Redis redis = BaseUtil.getRedis();
        if (redis.exists(token)) {
            Integer time = Const.MAX_USER_EXIST_TIME * 13;
            redis.expire(token, time);
            redis.expire(getUserToken(userInfo.getType(), userInfo.getUserId(), null), time);
        }
    }


    /**
     * @param token    用户token
     * @param userInfo 用户登录信息
     * @param type     type 1 PC登录 2 mobile登录
     */
    public static void userToken(String token, UserInfo userInfo, Integer type) {
        if (type == null) {
            type = 1;
        }
        Redis redis = BaseUtil.getRedis();
        String userToken;
        if (ObjectUtil.equal(2, type)) {
            String redisKey = Const.MULT_DEVICE_USER_TOKEN + userInfo.getUserId();
            redis.rpush(redisKey, token);
            userToken = Const.USER_TOKEN + ":" + type + ":" + userInfo.getUserId().toString() + ":" + token;
        } else {
            userToken = getUserToken(type, userInfo.getUserId(), null);
        }
        //查企业安全设置缓存
        AdminCompanySecuritySettingVO securitySettingVO = redis.get(Const.COMPANY_SECURITY_CONFIG_KEY+ ":" + 2);
        userExit(userInfo.getUserId(), type, 1, null, ObjectUtil.isNull(securitySettingVO) || ObjectUtil.equal(securitySettingVO.getMultDeviceLogin(), 1) ? 3 : 1);
        redis.setex(token, Const.MAX_USER_EXIST_TIME, userInfo);
        redis.setex(userToken, Const.MAX_USER_EXIST_TIME, token);
        Cookie cookie = new Cookie(Const.TOKEN_NAME, token);
        long second = DateUtil.betweenMs(new Date(), DateUtil.parseDate("2030-01-01")) / 1000L;
        cookie.setMaxAge((int) second);
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setDomain(Const.DEFAULT_DOMAIN.substring(Const.DEFAULT_DOMAIN.indexOf(".") + 1));
        BaseUtil.getResponse().addCookie(cookie);
    }

    /**
     * 设置临时 token
     *
     * @param userInfo 当前用户信息
     * @return
     */
    public static String setTempUserToken(UserInfo userInfo) {
        Redis redis = BaseUtil.getRedis();
        String token = Const.TEMP_TOKEN_PREFIX + IdUtil.simpleUUID();
        redis.setex(token, 300, userInfo);
        return token;
    }

    public static Long getSuperUser() {
        return getUser().getSuperUserId();
    }

    public static Long getSuperRole() {
        return getUser().getSuperRoleId();
    }

    public static boolean isAdmin() {
        UserInfo userInfo = getUser();
        return ObjectUtil.isNotEmpty(userInfo)
                && (
                (ObjectUtil.isNotEmpty(userInfo.getUserId()) && userInfo.getUserId().equals(userInfo.getSuperUserId()))
                        || (CollectionUtil.isNotEmpty(userInfo.getRoles()) && userInfo.getRoles().contains(userInfo.getSuperRoleId()))
        );
    }


    public static boolean isGroup() {
        UserInfo userInfo = getUser();
        return ObjectUtil.isNotEmpty(userInfo) && userInfo.getDeptId().equals(81100002L);
    }


    public static void userExit(Long userId, Integer type) {
        if (type == null) {
            for (Integer integer : Arrays.asList(1, 2, 3, 4)) {
                userExit(userId, integer, null, ObjectUtil.equal(2, integer) ? 1 : null, null);
            }
        } else {
            userExit(userId, type, null, ObjectUtil.equal(2, type) ? 1 : null, null);
        }

    }

    public static void userExit(Long userId, Integer type, Integer extra, Integer clearAll, Integer maxDeviceCount) {
        Redis redis = BaseUtil.getRedis();
        String token = null, key = getUserToken(type, userId, maxDeviceCount);
        if (!ObjectUtil.equal(2, type) && ObjectUtil.isNull(clearAll)) {
            if (redis.exists(key)) {
                token = redis.get(key);
                redis.del(key);
            }
            //1代表被挤掉提示
            if (Objects.equals(1, extra) && token != null) {
                Long time = redis.ttl(token);
                if (time > 1L) {
                    redis.setex(token, time.intValue(), new UserExtraInfo(1, DateUtil.formatDateTime(new Date())));
                }
            } else {
                if (token != null) {
                    redis.del(token);
                }
            }
        } else if (ObjectUtil.equal(2, type) && ObjectUtil.isNull(clearAll)) {
            if (ObjectUtil.isNull(maxDeviceCount)) {
                maxDeviceCount = 3;

            }
            //mobile
            String redisKey = Const.MULT_DEVICE_USER_TOKEN + userId;
            List<Object> range = redis.range(redisKey, 0, -1);
            redis.expire(redisKey, Const.MAX_USER_EXIST_TIME);
            if (CollUtil.isNotEmpty(range) && range.size() > maxDeviceCount) {
                String tobeRemoveToken = redis.lPop(redisKey);
                //清除最早登录缓存 token and userinfo
                if (StrUtil.isNotBlank(key) && redis.exists(key)) {
                    redis.del(key);
                }
                Long time = redis.ttl(tobeRemoveToken);
                if (time > 1L) {
                    redis.setex(tobeRemoveToken, time.intValue(), new UserExtraInfo(1,
                            DateUtil.formatDateTime(new Date())));
                }
            }

        } else {
            //mobile login and clear all
            String redisKey = Const.MULT_DEVICE_USER_TOKEN + userId;
            List<Object> range = redis.range(redisKey, 0, -1);
            for (Object tk : range) {
                String tobeRemoveKey = Const.USER_TOKEN + ":" + type + ":" + userId + ":" + tk.toString();
                if (redis.exists(tobeRemoveKey)) {
                    redis.del(tobeRemoveKey);
                    redis.del(tk);
                }
                redis.del(redisKey);
            }

        }

    }

    private static String getTobeRemoveToken(Long userId, Integer maxDeviceCount) {
        if (ObjectUtil.isNull(maxDeviceCount)) {
            maxDeviceCount = 3;
        }
        String redisKey = Const.MULT_DEVICE_USER_TOKEN + userId;
        Redis redis = BaseUtil.getRedis();
        List<Object> range = redis.range(redisKey, 0, -1);
        if (CollUtil.isNotEmpty(range) && range.size() > maxDeviceCount) {
            return redis.getKeyIndex(redisKey, 0);
        }
        return "";
    }

    /**
     * 获取用户登录的token标识
     *
     * @return key
     */
    private static String getUserToken(Integer type, Long userId, Integer maxDeviceCount) {
        String key = Const.USER_TOKEN + ":" + type + ":" + userId.toString();
        if (ObjectUtil.equal(2, type)) {
            String tobeRemoveToken = getTobeRemoveToken(userId, maxDeviceCount);
            if (StrUtil.isNotBlank(tobeRemoveToken)) {
                return key + ":" + tobeRemoveToken;
            } else {
                return "";
            }
        } else {
            return key;
        }

    }

    /**
     * 用户退出登录
     *
     * @param userInfo 用户信息
     * @param token    token
     */
    public static void userLogOut(UserInfo userInfo, String token) {
        Redis redis = BaseUtil.getRedis();
        Long userId = userInfo.getUserId();
        //删除权限缓存
        String[] keys = new String[]{token, AdminCacheKey.USER_AUTH_CACHE_KET + userId, CrmCacheKey.CRM_AUTH_USER_CACHE_KEY + userId, JxcCacheKey.JXC_AUTH_USER_CACHE_KEY + userId, ScrmCacheKey.SCRM_AUTH_USER_CACHE_KEY + userId, Const.NO_AUTH_MENU_CACHE_KEY + userId};
        //删除多设备登录缓存中当前设备的缓存信息
        if (redis.exists(Const.MULT_DEVICE_USER_TOKEN + userId)) {
            List<Object> range = redis.range(Const.MULT_DEVICE_USER_TOKEN + userId, 0, -1);
            redis.del(Const.MULT_DEVICE_USER_TOKEN + userId);
            List<Object> collect = range.stream().filter(obj -> !ObjectUtil.equal(obj, token)).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(collect)) {
                redis.rpushAll(Const.MULT_DEVICE_USER_TOKEN + userId, collect);
            }
            redis.expire(Const.MULT_DEVICE_USER_TOKEN + userId, Const.MAX_USER_EXIST_TIME);
        }
        redis.del((Object[]) keys);

    }

}
